﻿namespace Rsdn.SmartApp
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using JetBrains.Annotations;
    using Old;

    /// <summary>
    /// Extension instances cache.
    /// Thread safe.
    /// </summary>
    public class ExtensionsCache<TInfo, TElement> where TInfo : ElementInfo
    {
        private readonly Lazy<TElement[]> _cache;
        private readonly InstancingCustomParam[] _customParams;
        private readonly Func<TInfo, TElement> _instanceCreator;
        private readonly IServiceProvider _provider;

        /// <summary>
        /// Initialize instance with specified instance creator.
        /// </summary>
        public ExtensionsCache([NotNull] IServiceProvider provider, [CanBeNull] Func<TInfo, TElement> instanceCreator)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }
            this._provider = provider;
            this._customParams = EmptyArray<InstancingCustomParam>.Value;
            this._instanceCreator = instanceCreator;
            this._cache = new Lazy<TElement[]>((this._instanceCreator != null) ? new Func<TElement[]>(this.CreateElementsByCreator) : new Func<TElement[]>(this.CreateElementsByHelper));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExtensionsCache&lt;TInfo, TElement&gt;"/> class.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="customParams">The custom params.</param>
        public ExtensionsCache([NotNull] IServiceProvider provider, params InstancingCustomParam[] customParams)
            : this(provider, (Func<TInfo, TElement>)null)
        {
            this._customParams = customParams;
        }

        /// <summary>
        /// Creates the elements by creator.
        /// </summary>
        /// <returns></returns>
        private TElement[] CreateElementsByCreator()
        {
            TInfo[] extensionInfos = GetExtensionInfos();
            IEnumerable<TElement> enumerable = extensionInfos.Select(
                delegate(TInfo info)
                    {
                        return _instanceCreator(info);
                    });

            return enumerable.ToArray();
        }

        /// <summary>
        /// Creates the elements by helper.
        /// </summary>
        /// <returns></returns>
        private TElement[] CreateElementsByHelper()
        {
            IEnumerable<TElement> enumerable = GetExtensionInfos().Select(
                delegate(TInfo info)
                    {
                        return info.Type.CreateInstance<TElement>(_provider, _customParams);
                    });

            return enumerable.ToArray();
        }

        /// <summary>
        /// Returns all extension instances.
        /// </summary>
        /// <returns></returns>
        public TElement[] GetAllExtensions()
        {
            return _cache.Value;
        }

        /// <summary>
        /// Returns registered element infos.
        /// </summary>
        /// <returns></returns>
        public virtual TInfo[] GetExtensionInfos()
        {
            return _provider.GetRegisteredElements<TInfo>();
        }
    }
}

